package cn.sccdlg.springboot.mybatis.Service.impl;

import cn.sccdlg.springboot.mybatis.Service.UserService;
import cn.sccdlg.springboot.mybatis.mapper.UserMapper;
import cn.sccdlg.springboot.mybatis.model.User;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionTemplate;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.List;
@Service
public class UserServiceImpl implements UserService {
    private static Logger logger = org.slf4j.LoggerFactory.getLogger(UserServiceImpl.class);

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private SqlSessionFactory sqlSessionFactory;

    /**
     * 注入springboot默认配置好的RedisTemplate
     */
    @Autowired
    private RedisTemplate<Object, Object> redisTemplate;

    @SuppressWarnings("all")
    @Override
    public /*synchronized*/List<User> getAllUser() {
        //针对redis缓存中key进行字符串序列化优化
        RedisSerializer redisSerializer = new StringRedisSerializer();
        redisTemplate.setKeySerializer(redisSerializer);
        //优先查询redis缓存
        //高并发下，存在缓存穿透的问题，解决方案：1、方法中添加synchronized关键字（低效）；2、双重检测机制；
        // 缓存穿透指的是，当前请求的key在缓存中没有数值，导致访问直接到数据库，加大了数据库压力，
        // 影响系统运行稳定和性能，增加了宕机率
        List<User> userList = (List<User>)redisTemplate.opsForValue().get("allUser");
        //缓存中没有时，查询一下数据库
        //双重检测机制--双重检测锁
        if (null == userList){
            userList = (List<User>)redisTemplate.opsForValue().get("allUser");
            synchronized (this){
                if (null == userList){
                    userList = userMapper.getAllUser();
                    System.out.println("查询了数据库...");
                    //将查询的数据存入redis缓存
                    redisTemplate.opsForValue().set("allUser",userList);
                } else {
                    System.out.println("查询了缓存...1");
                }
            }
        } else {
            System.out.println("查询了缓存...2");
        }
        return userList;
    }

    @Transactional(rollbackFor = {Exception.class})
    @Override
    public void insertUsers(List<User> users) {
        if (CollectionUtils.isEmpty(users)) {
            logger.info("users is empty");
            return;
        }
        int count = 0;
        for (User user : users) {
            count += userMapper.insert(user);
        }
        logger.info("insertBatch count:{}", count);
    }

    @Transactional(rollbackFor = {Exception.class})
    @Override
    public void insertUsers2(List<User> users) {
        if (CollectionUtils.isEmpty(users)) {
            logger.info("users is empty");
            return;
        }
        userMapper.insertBatch(users);
        logger.info("insertBatch2 count:{}", users.size());
    }

    @Override
    public void insertUsers3(List<User> users) {
        if (CollectionUtils.isEmpty(users)) {
            logger.info("users is empty");
            return;
        }
        SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH, false);
        try {
            UserMapper mapper = session.getMapper(UserMapper.class);
            for (User data : users) {
                mapper.insert(data);
            }
            session.commit(); // 提交事务
            session.clearCache();
            logger.info("insertBatch3 count:{}", users.size());
        } catch (Exception e) {
            session.rollback(); // 回滚事务
            throw e;
        } finally {
            session.close();
        }
    }

}
